﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;

// TODO
// stop the smooth effect when the smooth movement is not perceptible
namespace DARE
{
    /// <summary>
    /// The node class is used for all transforms in 3d space.
    /// You can link differents objects to a node's entity and manipulate them in 3d throught the node.
    /// </summary>
    public class CNode : ARenderable, IPartitionerItem
    {

    #region identifier

        /// <summary>
        /// This is the three differents spaces for transformation.
        /// - LOCAL: apply rotation / translation relative to the node itself.
        /// - PARENT: apply rotation / translation relative to the parent's node.
        /// - WORLD: apply rotation / translation relative to the origin.
        /// </summary>
        public enum ESpace { LOCAL, PARENT, WORLD }

    #endregion

    #region fields

        // node general information
        protected CRender3D m_render3d;
        private CNode m_parent;
        private Dictionary<string, CNode> m_children;
        private CNodeModel m_nodeModel;
        private Matrix m_worldMatrix;
        private bool m_active;
        private bool m_visible;
        private int m_id;
        
        // derived transforms are the final transformations
        private Quaternion m_derivedOrientation;
        private Vector3 m_derivedPosition;
        private Vector3 m_derivedScale;

        // relative transforms are transformations relative to the parent
        private Quaternion m_relativeOrientation;
        private Vector3 m_relativePosition;
        private Vector3 m_relativeScale;

        // used to smooth the transforms
        private Quaternion m_derivedGoalOrientation;
        private Vector3 m_derivedGoalPosition;
        private Vector3 m_derivedGoalScale;

        // yaw pitch roll
        private float m_yaw;
        private float m_pitch;
        private float m_roll;
        private bool m_yawUpdated;
        private bool m_pitchUpdated;
        private bool m_rollUpdated;

        // should inherit orientation / scale
        private bool m_inheritOrientation;
        private bool m_inheritScale;

        // smooth
        private bool m_smoothPosition;
        private bool m_smoothOrientation;
        private bool m_smoothScale;
        private float m_smoothPositionAmount;
        private float m_smoothOrientationAmount;
        private float m_smoothScaleAmount;

        // interface implementation
        private BoundingBox m_aabb;
        private BoundingBox m_staticaabb;
        private bool m_moved;
        private EOrder m_order;
        event EventHandler ItemMoved;
        event EventHandler ItemStopped;
        private EType m_type;
        // berk... must be in the IpartitionerItem interface and not with this name... interface for the Draw method !
        private IPartitionerItemModel m_aabbModel;

        // LOD
        private int m_LOD;
        private float m_distFromCam;

        // able the physicalized nodes to move
        private bool m_ignorePhysicsTransform;
        private bool m_setPhysicsTranformCalled = false;
        private bool m_bodyEntityIsNull = false;

    #endregion

    #region implement

        /// <summary>
        /// Event raised when the node start to move.
        /// </summary>
        event EventHandler DARE.IPartitionerItem.eItemMoved
        {
            add
            {
                if (ItemMoved != null)
                    lock (ItemMoved)
                    {
                        ItemMoved += value;
                    }
                else
                    ItemMoved = new EventHandler(value);
            }
            remove
            {
                if (ItemMoved != null)
                    lock (ItemMoved)
                    {
                        ItemMoved -= value;
                    }
            }
        }

        /// <summary>
        /// Event raised when the node stop to move.
        /// </summary>
        event EventHandler DARE.IPartitionerItem.eItemStopped
        {
            add
            {
                if (ItemStopped != null)
                    lock (ItemStopped)
                    {
                        ItemStopped += value;
                    }
                else
                    ItemStopped = new EventHandler(value);
            }
            remove
            {
                if (ItemStopped != null)
                    lock (ItemStopped)
                    {
                        ItemStopped = value;
                    }
            }
        }

    #endregion

    #region properties

        /// <summary>
        /// LOD, 0 is near the camera, 10 is the farest.
        /// See Partitionner, LODRange to change the LOD values.
        /// Get the LOD.
        /// Set the LOD, used to test.
        /// </summary>
        public int LOD
        {
            get { return m_LOD; }
            set { m_LOD = value; }
        }

        /// <summary>
        /// Distance squared from the camera to the node.
        /// This value is calculated in the Partitionner.
        /// Get the distance.
        /// Set the distance, used to test.
        /// </summary>
        public float DistFromCam
        {
            get { return m_distFromCam; }
            set { m_distFromCam = value; }
        }

        /// <summary>
        /// Model that represent the collision view box.
        /// Get the AABBModel.
        /// Set the AABBModel.
        /// </summary>
        public IPartitionerItemModel AABBModel
        {
            get { return m_aabbModel; }
            set { m_aabbModel = value; }
        }

        /// <summary>
        /// The type of the node can be STATIC or DYNAMIC.
        /// A static node is processed in the partitionner, and then updated and draw only when in the view frustrum.
        /// A dynamic node is always updated, and draw when in the view frustrum.
        /// Statics nodes are a lot faster to process, but moving them takes more time than a dynamic one.
        /// For example if you have a house with a character the can move inside, all the decoratives objects should be set 
        /// to static, the character should be set to dynamic since he can often moves.
        /// </summary>
        public EType Type
        {
            get { return m_type; }
            set
            {
                if (m_type == value)
                    return;
                if (m_render3d != null)
                {
                    if (m_type == EType.STATIC &&
                        !m_render3d.DynamicNodes.ContainsKey(m_name))
                    {
                        m_render3d.DynamicNodes.Add(m_name, this);
                        m_render3d.Partitioner.RemoveItem(this);
                    }
                    else if (m_type == EType.DYNAMIC &&
                        m_render3d.DynamicNodes.ContainsKey(m_name))
                    {
                        m_render3d.DynamicNodes.Remove(m_name);
                        m_render3d.Partitioner.AddItem(this);
                    }
                    m_type = value;
                }
                else
                    m_type = value;
            }
        }

        /// <summary>
        /// The unique identifier of the node.
        /// Get the id.
        /// </summary>
        public int Id
        {
            get { return m_id; }
        }

        /// <summary>
        /// Order the node in the render list.
        /// See IPartitionnerItem EOrder for more informations.
        /// The order may automaticaly change to BACKTOFRONT when the node is for example set to transparent.
        /// Get the order.
        /// Set the order.
        /// </summary>
        public EOrder Order
        {
            get { return m_order; }
            set { m_order = value; }
        }

        /// <summary>
        /// Parent's node.
        /// Each node contain a parent, when created without parent, the node refers to the origin node's 
        /// of the render as his parent.
        /// Get the parent's node.
        /// Set the parent's node, the node will be moved to the parent's position plus the actual position of the node.
        /// </summary>
        public CNode Parent
        {
            get { return m_parent; }
            set { SetParent(value); }
        }
        
        /// <summary>
        /// Render3D associated to the node.
        /// Get the Render3D.
        /// Set the Render3D.
        /// </summary>
        public CRender3D Render3D
        {
            get { return m_render3d; }
            internal set { m_render3d = value; }
        }

        /// <summary>
        /// Get all the children of the node.
        /// </summary>
        public Dictionary<string, CNode> Children
        {
            get { return m_children; }
        }

        /// <summary>
        /// The worldmatrix contains all the transformations of the node.
        /// Get the matrix.
        /// Set the matrix, only used by physics to test.
        /// </summary>
        public Matrix WorldMatrix
        {
            get { return m_worldMatrix; }
            set { m_worldMatrix = value; } // should disapear, used to test physic
        }

        /// <summary>
        /// Each node contains a model that represent his axes.
        /// Red axis is the correspond to the yaw, green the pitch, blue to roll.
        /// </summary>
        public CNodeModel NodeModel
        {
            get { return m_nodeModel; }
        }

        /// <summary>
        /// An active node is drawn / updated.
        /// When set to inactive, the node will be like it was removed from the render: 
        /// it will not be draw, nor updated and physics is disabled. But the node is not 
        /// removed from the render so you can still get it and reactivate it when you want.
        /// Get if the node is active.
        /// Set active or not.
        /// </summary>
        public bool IsActive
        {
            get { return m_active; }
            set
            {
                m_active = value;
                if (m_entity.Physic != null)
                {
                    if (!m_active)
                        m_entity.Physic.Body.DisableBody();
                    else
                        m_entity.Physic.Body.EnableBody();
                }
                foreach (CNode child in m_children.Values)
                    child.IsActive = m_active;
            }
        }

        /// <summary>
        /// A visible node will render with his model / particles / sprites...
        /// Get if the node is visible.
        /// Set the visibilty of the node.
        /// </summary>
        public bool IsVisible
        {
            get { return m_visible; }
            set { SetVisible(value); }
        }

        /// <summary>
        /// Old implementation, same as visible.
        /// </summary>
        public bool Drawable
        {
            get { return m_drawable; }
            set
            {
                m_drawable = value;
                SetVisible(value);
            }
        }

        /// <summary>
        /// An updatable node will update his enity.
        /// Get if the node is updated.
        /// Set if the node will be updated.
        /// </summary>
        public bool Updatable
        {
            get { return m_updatable; }
            set { m_updatable = value; }
        }

        /// <summary>
        /// Get or set if the orientation is applied to the children.
        /// </summary>
        public bool InheritOrientation
        {
            get { return m_inheritOrientation; }
            set { m_inheritOrientation = value; }
        }

        /// <summary>
        /// Get or set if the scale is applied to the children.
        /// </summary>
        public bool InheritScale
        {
            get { return m_inheritScale; }
            set { m_inheritScale = value; }
        }

        /// <summary>
        /// Get or set if the node is smoothed. Smooth is not inherited by children.
        /// The position, orientation and scale will be affected.
        /// </summary>
        public bool Smooth
        {
            get { return m_smoothPosition || m_smoothOrientation || m_smoothScale; }
            set { m_smoothPosition = m_smoothOrientation = m_smoothScale = value; }
        }

        /// <summary>
        /// Get or set if the node and his children is smoothed.
        /// Position, orientation and scale will be affected.
        /// </summary>
        public bool SmoothChildren
        {
            get { return Smooth; }
            set
            {
                Smooth = value;
                foreach (CNode child in m_children.Values)
                    child.SmoothChildren = value;
            }
        }

        /// <summary>
        /// A smooth amount of 1 will have no effect.
        /// More the smooth amount is near 0, more the smooth is efficient.
        /// Get or set the smooth amount.
        /// </summary>
        public float SmoothAmount
        {
            get { return (m_smoothPositionAmount + m_smoothOrientationAmount + m_smoothScaleAmount) / 3; }
            set { m_smoothPositionAmount = m_smoothOrientationAmount = m_smoothScaleAmount = MathHelper.Clamp(value, 0, 1); }
        }

        /// <summary>
        /// Set the smooth amount on the children.
        /// Get the smooth amount on the children.
        /// </summary>
        public float SmoothAmountChildren
        {
            get { return SmoothAmount; }
            set
            {
                SmoothAmount = value;
                foreach (CNode child in m_children.Values)
                    child.SmoothAmountChildren = value;
            }
        }

        /// <summary>
        /// Smooth only the position. Not inherited by children.
        /// Get or set to smooth the position.
        /// </summary>
        public bool SmoothPosition
        {
            get { return m_smoothPosition; }
            set { m_smoothPosition = value; }
        }

        /// <summary>
        /// Smooth children's positions.
        /// Get or set if the children's positions are smoothed.
        /// </summary>
        public bool SmoothPositionChildren
        {
            get { return SmoothPosition; }
            set
            {
                SmoothPosition = value;
                foreach (CNode child in m_children.Values)
                    child.SmoothPositionChildren = value;
            }
        }

        /// <summary>
        /// Smooth only the orientation. Not inherited by children.
        /// Get or set to smooth the orientation.
        /// </summary>
        public bool SmoothOrientation
        {
            get { return m_smoothOrientation; }
            set { m_smoothOrientation = value; }
        }

        /// <summary>
        /// Smooth children's orientation.
        /// Get or set if the children's orientation are smoothed.
        /// </summary>
        public bool SmoothOrientationChildren
        {
            get { return SmoothOrientation; }
            set
            {
                SmoothOrientation = value;
                foreach (CNode child in m_children.Values)
                    child.SmoothOrientationChildren = value;
            }
        }

        /// <summary>
        /// Smooth only the scale. Not inherited bu children.
        /// Get or set to smooth the scale.
        /// </summary>
        public bool SmoothScale
        {
            get { return m_smoothScale; }
            set { m_smoothScale = value; }
        }

        /// <summary>
        /// Smooth children's scales.
        /// Get or set to smooth the children's scales.
        /// </summary>
        public bool SmoothScaleChildren
        {
            get { return SmoothScale; }
            set
            {
                SmoothScale = value;
                foreach (CNode child in m_children.Values)
                    child.SmoothScale = value;
            }
        }

        /// <summary>
        /// Position smooth amount. Work only if SmoothPosition is true.
        /// A smooth amount of 1 will have no effect, more the smooth is near 0, more the smooth is strong.
        /// Get or set the amount of position's smooth.
        /// </summary>
        public float SmoothPositionAmount
        {
            get { return m_smoothPositionAmount; }
            set { m_smoothPositionAmount = MathHelper.Clamp(value, 0, 1); }
        }

        /// <summary>
        /// Children smooth position amount. Work only if SmoothPositionChildren is true.
        /// A smooth amount of 1 will have no effect, more the smooth is near 0, more the smooth is strong.
        /// Get or set the amount of children's position smooth.
        /// </summary>
        public float SmoothPositionAmountChildren
        {
            get { return SmoothPositionAmount; }
            set
            {
                SmoothPositionAmount = value;
                foreach (CNode child in m_children.Values)
                    child.SmoothPositionAmountChildren = value;
            }
        }

        /// <summary>
        /// Orientation smooth amount. Work only if SmoothOrientation is true.
        /// A smooth amount of 1 will have no efffect, more the smooth is near 0, more the smooth is strong.
        /// Get or set the amount of orientation's smooth.
        /// </summary>
        public float SmoothOrientationAmount
        {
            get { return m_smoothOrientationAmount; }
            set { m_smoothOrientationAmount = MathHelper.Clamp(value, 0, 1); }
        }

        /// <summary>
        /// Children orientation smooth amount. Work only if SmoothOrientationChildren is true.
        /// A smooth amount of 1 will have no effect, more the smooth is near 0, more the smooth is strong.
        /// Get or set the amount of children's orientation smooth.
        /// </summary>
        public float SmoothOrientationAmountChildren
        {
            get { return SmoothOrientationAmount; }
            set
            {
                SmoothOrientationAmount = value;
                foreach (CNode child in m_children.Values)
                    child.SmoothOrientationAmountChildren = value;
            }
        }

        /// <summary>
        /// Scale smooth amount. Work only if SmoothScale is true.
        /// A smooth amount of 1 will have no effect, more the smooth is near 0, more the smooth is strong.
        /// Get or set the amount of scale's smooth.
        /// </summary>
        public float SmoothScaleAmount
        {
            get { return m_smoothScaleAmount; }
            set { m_smoothScaleAmount = MathHelper.Clamp(value, 0, 1); }
        }

        /// <summary>
        /// Children smooth scale amount. Work only if SmoothScaleChildren is true.
        /// A smooth amount of 1 will have no effect, more the smooth is near 0, more the smooth is strong.
        /// Get or set the amount of children scale's smooth.
        /// </summary>
        public float SmoothScaleAmountChildren
        {
            get { return SmoothScaleAmount; }
            set
            {
                SmoothScaleAmount = value;
                foreach (CNode child in m_children.Values)
                    child.SmoothScaleAmountChildren = value;
            }
        }

        /// <summary>
        /// The AABB box is used by the Partitioner. It is the viewable bounding box.
        /// Get is from world bounding box
        /// Set is from local position
        /// </summary>
        public BoundingBox AABB
        {
            get
            {
                //if (m_moved)
                //{
                    m_aabb.Min = m_staticaabb.Min + m_derivedPosition;
                    m_aabb.Max = m_staticaabb.Max + m_derivedPosition;
                //}
                return m_aabb;
            }
            // 
            set // because when changing the AABB the octreeitem should change his position in the octree
            {
                if (value != m_staticaabb)
                {
                    if (m_type == EType.STATIC && m_render3d != null)
                        m_render3d.RemoveNode(this);
                    m_staticaabb = value;
                    if (m_type == EType.STATIC && m_render3d != null)
                        m_render3d.AddNode(this);
                }
            }
        }

        /// <summary>
        /// The node position have to be set before this call
        /// Set the AABB from an absolute position.
        /// </summary>
        public BoundingBox AABBFromWorldBoundingBox
        {
            set
            {
                if (m_type == EType.STATIC && m_render3d != null)
                    m_render3d.RemoveNode(this);
                m_staticaabb.Min = value.Min - Position;
                m_staticaabb.Max = value.Max - Position;
                if (m_type == EType.STATIC && m_render3d != null)
                    m_render3d.AddNode(this);
            }
        }

        /// <summary>
        /// Get if the node moved at this turn.
        /// </summary>
        public bool Moved
        {
            get { return m_moved; }
        }

    #endregion

    #region properties position/orientation

        /// <summary>
        /// Get the orientation relative to world.
        /// Set the orientation relative to world.
        /// </summary>
        public Quaternion Orientation
        {
            get { return m_derivedOrientation; }
            set
            {
                if (m_derivedGoalOrientation != value)
                    SetOrientation(value, ESpace.WORLD);
            }
        }

        /// <summary>
        /// Get the postion relative to world.
        /// Set the position relative to world.
        /// </summary>
        public Vector3 Position
        {
            get { return m_derivedPosition; }
            set 
            {
                if (m_derivedGoalPosition != value)
                    SetPosition(value, ESpace.WORLD);
            }
        }

        /// <summary>
        /// Get or set the derived scale.
        /// </summary>
        public Vector3 NScale
        {
            get { return m_derivedScale; }
            set
            {
                if (m_derivedGoalScale != value)
                    SetScale(value);
            }
        }

        /// <summary>
        /// Get or set the relative orientation. (relative to parent)
        /// </summary>
        public Quaternion RelativeOrientation
        {
            get { return m_relativeOrientation; }
            set { SetOrientation(value, ESpace.PARENT); }
        }

        /// <summary>
        /// Get or set the relative position. (relative to parent)
        /// </summary>
        public Vector3 RelativePosition
        {
            get { return m_relativePosition; }
            set { SetPosition(value, ESpace.PARENT); }
        }

        /// <summary>
        /// Get or set the relative scale. (relative to parent)
        /// </summary>
        public Vector3 RelativeScale
        {
            get { return m_relativeScale; }
            set { m_relativeScale = value; }
        }

        /// <summary>
        /// Get the yaw relative to the world.
        /// Set the yaw relative to the world.
        /// </summary>
        public float NYaw
        {
            get
            { 
                if (!m_yawUpdated)
                {
                    Vector3 dirxz = GetLocalAxisZ();

                    dirxz.Y = 0;
                    dirxz.Normalize();
                    float ag0 = (float)Math.Acos(-dirxz.Z);
                    float ag1 = (float)Math.Asin(-dirxz.X);                
                    if (float.IsNaN(ag0) || float.IsNaN(ag1)) // should display a warning !!!!
                        return 0;
                    m_yaw = ag0 * Math.Sign(ag1);
                    m_yawUpdated = true;
                }
                return m_yaw;
            }
            set
            {
                Orientation = Quaternion.CreateFromYawPitchRoll(value, NPitch, NRoll);
            }
        }

        /// <summary>
        /// Get the pitch relative to the world.
        /// Set the pitch relative to the world.
        /// </summary>
        public float NPitch
        {
            get
            {
                if (!m_pitchUpdated)
                {
                    Vector3 diryz = GetLocalAxisY();

                    diryz.X = 0;
                    diryz.Normalize();
                    float ag0 = (float)Math.Acos(-diryz.Z);
                    float ag1 = (float)Math.Asin(-diryz.Y);
                    if (float.IsNaN(ag0) || float.IsNaN(ag1)) // should display a warning !!!!
                        return 0;
                    m_pitch = ag0 * Math.Sign(-ag1);
                    m_pitchUpdated = true;
                }
                return m_pitch;
            }
            set
            {
                Orientation = Quaternion.CreateFromYawPitchRoll(NYaw, value, NRoll);
            }
        }

        /// <summary>
        /// Approximative at the moment !!!
        /// Get the roll relative to the world.
        /// Set the roll relative to the world.
        /// </summary>
        public float NRoll
        {
            get
            {
                if (!m_rollUpdated)
                {
                    Vector3 dirxy = GetLocalAxisX();
                    Quaternion conjugate = Quaternion.CreateFromYawPitchRoll(NYaw, NPitch, 0);
                    conjugate.Conjugate();
                    dirxy = Vector3.Transform(dirxy, conjugate);

                    dirxy.Z = 0;
                    dirxy.Normalize();
                    float ag0 = (float)Math.Acos(dirxy.Y);
                    float ag1 = (float)Math.Asin(dirxy.X);
                    if (float.IsNaN(ag0) || float.IsNaN(ag1))
                        return 0;

                    m_roll = ag0;

                    float y = 2 * m_derivedOrientation.Y;
                    float z = 2 * m_derivedOrientation.Z;
                    float wz = z * m_derivedOrientation.W;
                    float xy = y * m_derivedOrientation.X;
                    float yy = y * m_derivedOrientation.Y;
                    float zz = z * m_derivedOrientation.Z;
                    m_roll = (float)Math.Atan2(xy + wz, 1 - (yy + zz));

                    m_rollUpdated = true;
                }
                return m_roll;
            }
            set
            {
                Orientation = Quaternion.CreateFromYawPitchRoll(NYaw, NPitch, value);
            }
        }

    #endregion

    #region ctor

        /// <summary>
        /// Create the node.
        /// </summary>
        /// <param name="type">Type of the node, STATIC or DYNAMIC, default value is STATIC.</param>
        /// <param name="createEntity">Create or not the entity associated to the node, default value is true.</param>
        public CNode(EType type, bool createEntity)
        {
            m_id = CDare.Instance.IdCounter;
            Initialize(m_id.ToString(), type, createEntity);
        }

        public CNode()
            : this(EType.STATIC, true)
        {
        }

        public CNode(EType type)
            : this(type, true)
        {

        }

        /// <summary>
        /// Create the node.
        /// </summary>
        /// <param name="name">Node's name.</param>
        /// <param name="type">Type of the node, STATIC or DYNAMIC, default value is STATIC.</param>
        /// <param name="createEntity">Create or not the entity associated to the node, default value is true.</param>
        public CNode(string name, EType type, bool createEntity)
        {
            if (CDare.Instance != null)
                m_id = CDare.Instance.IdCounter;
            Initialize(name, type, createEntity);
        }

        public CNode(string name)
            : this(name, EType.STATIC, true)
        {

        }

        public CNode(string name, EType type)
            : this(name, type, true)
        {

        }

        /// <summary>
        /// Private method to initialize the node with the specified parameters.
        /// </summary>
        /// <param name="name">Name of the node.</param>
        /// <param name="render3d">render 3d.</param>
        /// <param name="createEntity">Create or not the entity associated with the node.</param>
        private void Initialize(string name, EType type, bool createEntity)
        {
            // default aabb value.
            m_aabb = new BoundingBox(-Vector3.One / 2, Vector3.One / 2);
            m_staticaabb = new BoundingBox();
            m_staticaabb.Min = m_aabb.Min;
            m_staticaabb.Max = m_aabb.Max;
            m_moved = false;

            m_render3d = null;
            m_parent = null;
            m_children = new Dictionary<string, CNode>();
            m_derivedOrientation = Quaternion.Identity;
            m_relativeOrientation = Quaternion.Identity;
            m_derivedGoalOrientation = Quaternion.Identity;
            m_derivedPosition = Vector3.Zero;
            m_relativePosition = Vector3.Zero;
            m_derivedGoalPosition = Vector3.Zero;
            m_derivedScale = Vector3.One;
            m_relativeScale = Vector3.One;
            m_derivedGoalScale = Vector3.One;
            m_worldMatrix = Matrix.Identity;
            m_inheritOrientation = true;
            m_inheritScale = true;
            m_nodeModel = new CNodeModel();
            m_visible = true;
            m_yawUpdated = false;
            m_pitchUpdated = false;
            m_rollUpdated = false;
            Smooth = false;
            SmoothAmount = 0.02f;
            m_order = EOrder.REGULAR;
            m_type = type;
            m_ignorePhysicsTransform = true;

            m_name = name;
            m_node = this;
            m_active = true;
            m_aabbModel = null;
            if (createEntity == true)
                CreateEntity();
            m_parent = CRender3D.RefNode;
        }

        /// <summary>
        /// Change the node's name.
        /// </summary>
        /// <param name="name">Name of the node</param>
        public CNode SetName(string name)
        {
            if ((m_name != name) && (m_render3d != null) &&
                m_render3d.Contains(m_name) &&
                !m_render3d.Contains(name))
            {
                m_render3d.RemoveNode(this);
                m_name = name;
                m_render3d.AddNode(this);
            }
            else if (m_render3d == null)
                m_name = name;
            else
                throw new Exception("Error setting node name " + name + " !");
            return this;
        }

        /// <summary>
        /// Clone a node without his children and with a generated name.
        /// The entity is cloned too, warning, cloning is forbidden by laws :D
        /// (but YOU are the law here).
        /// </summary>
        /// <returns>The cloned node</returns>
        public CNode ShalowClone()
        {
            CNode cloneNode = null;
            if (m_render3d != null)
                cloneNode = m_render3d.CreateNode(m_type, true);
            else
                cloneNode = new CNode(m_type, true);

            cloneNode.Position = m_derivedPosition;
            cloneNode.Orientation = m_derivedOrientation;
            cloneNode.NScale = m_derivedScale;
            if (m_entity != null)
            {
                if (m_render3d == null)
                    throw new Exception("Warning! Render3D is null and entity isn't");
                if (m_entity.Physic != null)
                    cloneNode.Entity.AttachPhysic(m_entity.Physic.Clone());
                if (m_entity.Model != null)
                    cloneNode.Entity.AttachModel(m_entity.Model.Clone());
#if !WINDOWS_PHONE
                foreach (CSound sound in m_entity.Sound.Values)
                    cloneNode.Entity.AttachSound(sound.Clone());
#endif
                foreach (AEmitter emitter in m_entity.Emitter.Values)
                    cloneNode.Entity.AttachEmitter(emitter.Clone());
                foreach (CSprite3D sprite in m_entity.Sprite3D.Values)
                    cloneNode.Entity.AttachSprite3D(sprite.Clone());
            }
            return cloneNode;
        }

        public object Clone()
        {
            return null;
        }

    #endregion

    #region run
        
        /// <summary>
        /// Should be the true update, but used for test atm.
        /// </summary>
        /// <param name="tc"></param>
        public void Update(CTimeConfig tc)
        {
            // old stuff here
        }

        /// <summary>
        /// Update the node and his entity.
        /// </summary>
        /// <param name="gameTime">The gametime yea !</param>
        public override void Update(GameTime gameTime)
        {
            if (!m_active)
                return;
            if (!m_updatable)
                return;
            // here because we need to draw the node model to debug, but should not be draw if the node is hidden, less calculation :)
            m_worldMatrix = Matrix.Identity;
            m_worldMatrix *= Matrix.CreateScale(m_derivedScale);
            m_worldMatrix *= Matrix.CreateFromQuaternion(m_derivedOrientation);
            m_worldMatrix *= Matrix.CreateTranslation(m_derivedPosition);
            if (m_entity != null)
                m_entity.Update(gameTime);
            if (Smooth)
                _UpdateTransform(true, true, true);
            // if the physics transformations are ignored, we move it by force
            if (m_ignorePhysicsTransform && m_entity != null && m_entity.Physic != null)
            {
                //CDare.Instance.DebugMgr.DebugConsole.WriteLine("Physics MoveTo called !");
                m_entity.Physic.Body.MoveTo(m_derivedPosition, Matrix.CreateFromQuaternion(m_derivedOrientation));
            }
        }

        /// <summary>
        /// Draw the node here, nodemodel, and entity.
        /// </summary>
        /// <param name="gameTime">Gametime.</param>
        public override void Draw(GameTime gameTime)
        {
            if (!m_active)
                return;
            if (m_drawable && m_visible)
            {
                if (m_render3d != null && m_render3d.CameraMgr.ActiveCamera == null)
                    return;
                if (m_entity != null)
                    m_entity.Draw(gameTime);
            }
            if (m_render3d != null)
                if (m_nodeModel.Show = m_render3d.ShowModelNode)
                    m_nodeModel.Draw(m_worldMatrix, m_render3d.CameraMgr.ActiveCamera.View, m_render3d.CameraMgr.ActiveCamera.Projection);
        }

    #endregion

    #region REVIEW static utils

        /// <summary>
        /// WARNING NOT FINISHED !
        /// Omg wtf this is not finished, return pitch and roll from a vector, you can use it anyway.
        /// </summary>
        /// <param name="vector">Da vector.</param>
        /// <param name="pitch">Pitch.</param>
        /// <param name="yaw">Yaw.</param>
        /// <param name="roll">Roll.</param>
        public static void VectorToAngle(Vector3 vector, out float pitch, out float yaw, out float roll)
        {
            pitch = (float)Math.Acos((double)vector.Z / (double)1);
            roll = (float)Math.Acos((double)vector.X / (double)1);
            yaw = 0.0f;
        }

        /// <summary>
        /// WARNING NOT FINISHED !
        /// Return a quaternion from a normal, uses VectorToAngle to do this.
        /// <seealso cref="VectorToAngle"/>
        /// </summary>
        /// <param name="normal">Normal.</param>
        /// <returns>Quaternion orientation.</returns>
        public static Quaternion NormalToNodeOrientation(Vector3 normal)
        {
            float yaw, pitch, roll;

            VectorToAngle(normal, out pitch, out yaw, out roll);
            roll -= MathHelper.PiOver2;
            pitch -= MathHelper.PiOver2;

            Matrix rotation = Matrix.CreateRotationX(-pitch) * Matrix.CreateRotationZ(roll);
            return Quaternion.CreateFromRotationMatrix(rotation);
        }

        /// <summary>
        /// Scale an angle in degree between -180.0 and 180.0.
        /// </summary>
        /// <param name="value">Angle</param>
        /// <returns>Scaled angle</returns>
        public static float Warp(float value)
        {
            while (value > 180.0f)
                value -= 360.0f;
            while (value < -180.0f)
                value += 360.0f;
            return value;
        }

        /// <summary>
        /// Get the yaw of a quaternion.
        /// <remarks>
        /// WARNING Not really tested, good luck :).
        /// </remarks>
        /// </summary>
        /// <param name="q">The quaternion from wich you want to extract the yaw.</param>
        /// <returns>Yaw of the quaternion.</returns>
        public static float GetYaw(Quaternion q)
        {
            float x = 2 * q.X;
            float y = 2 * q.Y;
            float z = 2 * q.Z;
            float wy = y * q.W;
            float xx = x * q.X;
            float xz = z * q.X;
            float yy = y * q.Y;
            return (float)Math.Atan2(xz + wy, 1 - (xx + yy));
        }

        /// <summary>
        /// Get the pitch of a quaternion.
        /// <remarks>
        /// WARNING Not really tested, good luck :).
        /// </remarks>
        /// </summary>
        /// <param name="q">The quaternion from wich you want to extract the pitch.</param>
        /// <returns>Pitch of the quaternion.</returns>
        public static float GetPitch(Quaternion q)
        {
            float x = 2 * q.X;
            float z = 2 * q.Z;
            float wx = x * q.W;
            float xx = x * q.X;
            float yz = z * q.Y;
            float zz = z * q.Z;
            return (float)Math.Atan2(yz + wx, 1 - (xx + zz));
        }

        /// <summary>
        /// Get the roll of a quaternion.
        /// <remarks>
        /// WARNING Not really tested, good luck :).
        /// </remarks>
        /// </summary>
        /// <param name="q">The quaternion from wich you want to extract the roll.</param>
        /// <returns>Roll of the quaternion.</returns>
        public static float GetRoll(Quaternion q)
        {
            float y = 2 * q.Y;
            float z = 2 * q.Z;
            float wz = z * q.W;
            float xy = y * q.X;
            float yy = y * q.Y;
            float zz = z * q.Z;
            return (float)Math.Atan2(xy + wz, 1 - (yy + zz));
        }

        /// <summary>
        /// Rotate a given point around a vector with an angle.
        /// </summary>
        /// <param name="point">Point to rotate.</param>
        /// <param name="axis">Rotate the point around this vector.</param>
        /// <param name="angle">Rotation angle in radian.</param>
        /// <returns>Rotated point.</returns>
        public static Vector3 RotatePointAroundVector(Vector3 point, Vector3 axis, float angle)
        {
            float x = point.X, y = point.Y, z = point.Z, u = axis.X, v = axis.Y, w = axis.Z, resX, resY, resZ;
            angle = MathHelper.ToRadians(angle);

            resX = u * (u*x + v*y + w*z) + (x * (v*v + w*w) - u * (v*y + w*z)) * (float)Math.Cos((double)angle) + (-w*y + v*z) * (float)Math.Sin((double)angle);
            resY = v * (u*x + v*y + w*z) + (y * (u*u + w*w) - v * (u*x + w*z)) * (float)Math.Cos((double)angle) + (w*x - u*z) * (float)Math.Sin((double)angle);
            resZ = w * (u*x + v*y + w*z) + (z * (u*u + v*v) - w * (u*x + v*y)) * (float)Math.Cos((double)angle) + (-v*x + u*y ) * (float)Math.Sin((double)angle);

            return new Vector3(resX, resY, resZ);
        }

        /// <summary>
        /// Rotate a point p around a segment [p1, p2] by an given angle.
        /// </summary>
        /// <param name="p">Point to rotate.</param>
        /// <param name="p1">First coordinate of the segment.</param>
        /// <param name="p2">Second coordinate of the segment.</param>
        /// <param name="angle">Rotation angle in radian.</param>
        /// <returns>Rotated point.</returns>
        public static Vector3 RotatePointAroundLine(Vector3 p, Vector3 p1, Vector3 p2, float angle)
        {
            Vector3 q = new Vector3();
            Vector3 r = new Vector3();
            float cosAngle, sinAngle;

            r.X = p2.X - p1.X;
            r.Y = p2.Y - p1.Y;
            r.Z = p2.Z - p1.Z;
            p.X -= p1.X;
            p.Y -= p1.Y;
            p.Z -= p1.Z;
            r.Normalize();

            cosAngle = (float)Math.Cos((double)MathHelper.ToRadians(angle));
            sinAngle = (float)Math.Sign((double)MathHelper.ToRadians(angle));

            q.X = (cosAngle + (1 - cosAngle) * r.X * r.X) * p.X + 
                ((1 - cosAngle) * r.X * r.Y - r.Z * sinAngle) * p.Y + 
                ((1 - cosAngle) * r.X * r.Z + r.Y * sinAngle) * p.Z;
            q.Y = ((1 - cosAngle) * r.X * r.Y + r.Z * sinAngle) * p.X + 
                (cosAngle + (1 - cosAngle) * r.Y * r.Y) * p.Y + 
                ((1 - cosAngle) * r.Y * r.Z - r.X * sinAngle) * p.Z;
            q.Z = ((1 - cosAngle) * r.X * r.Z - r.Y * sinAngle) * p.X + 
                ((1 - cosAngle) * r.Y * r.Z + r.X * sinAngle) * p.Y + 
                (cosAngle + (1 - cosAngle) * r.Z * r.Z) * p.Z;
            q.X += p1.X;
            q.Y += p1.Y;
            q.Z += p1.Z;
            return q;
        }

        /// <summary>
        /// Return the local X axis of the node.
        /// </summary>
        /// <returns>X axis.</returns>
        public Vector3 GetLocalAxisX()
        {
            return Vector3.Transform(Vector3.UnitX, m_derivedOrientation);
        }

        /// <summary>
        /// Return the local Y axis of the node.
        /// </summary>
        /// <returns>Y axis.</returns>
        public Vector3 GetLocalAxisY()
        {
            return Vector3.Transform(Vector3.UnitY, m_derivedOrientation);
        }

        /// <summary>
        /// Return the local Z axis of the node.
        /// </summary>
        /// <returns>Z axis.</returns>
        public Vector3 GetLocalAxisZ()
        {
            return Vector3.Transform(Vector3.UnitZ, m_derivedOrientation);
        }

        /// <summary>
        /// WARNING not tested !
        /// Transform a quaternion to an axis / angle.
        /// </summary>
        /// <param name="q">Quaternion to transform.</param>
        /// <param name="axis">Axis result.</param>
        /// <param name="angle">Angle result in radian.</param>
        static public void QuaternionToAxisAngle(Quaternion q, out Vector3 axis, out float angle)
        {
            axis = new Vector3();
            if (q.W > 1)
                q.Normalize();
            angle = 2 * (float)Math.Acos((double)q.W);

            double s = Math.Sqrt(1 - q.W * q.W);

            if (s < 0.001)
            {
                axis.X = q.X;
                axis.Y = q.Y;
                axis.Z = q.Z;
            }
            else
            {
                axis.X = q.X / (float)s;
                axis.Y = q.Y / (float)s;
                axis.Z = q.Z / (float)s;
            }
        }

        /// <summary>
        /// WARNING not tested !
        /// Transform an axis / angle to yaw pitch roll.
        /// </summary>
        /// <param name="axis">Axis to transform.</param>
        /// <param name="angle">Angle to transform.</param>
        /// <param name="yaw">Yaw in radian.</param>
        /// <param name="pitch">Pitch in radian.</param>
        /// <param name="roll">Roll in radian.</param>
        static public void AxisAngleToEulerAngle(Vector3 axis, float angle, out float yaw, out float pitch, out float roll)
        {
            float s = (float)Math.Sin((double)angle);
            float c = (float)Math.Cos((double)angle);
            float t = 1 - c;

            if ((axis.X *axis.Y * t + axis.Z * s) > 0.998f) // notrh pole
            {
                yaw = 2 * (float)Math.Atan2((double)axis.X * Math.Sin((double)(angle / 2)), 
                    Math.Cos((double)(angle / 2)));
                roll = (float)Math.PI / 2;
                pitch = 0;
            }
            else if ((axis.X * axis.Y * t + axis.Z) < -0.998f) // south pole
            {
                yaw = -2 * (float)Math.Atan2((double)axis.X * Math.Sin((double)(angle / 2)), 
                    Math.Cos((double)(angle / 2)));
                roll = -(float)Math.PI / 2;
                pitch = 0;
            }
            else
            {
                yaw = (float)Math.Atan2((double)(axis.Y * s - axis.X * axis.Z * t), 
                    (double)(1 - (axis.Y * axis.Y + axis.Z * axis.Z) * t));
                roll = (float)Math.Asin((double)(axis.X * axis.Y * t + axis.Z * s));
                pitch = (float)Math.Atan2((double)(axis.X * s - axis.Y * axis.Z * t), 
                    (double)(1 - (axis.X * axis.X + axis.Z * axis.Z) * t));
            }
        }

    #endregion

    #region entity
 
        /// <summary>
        /// Attach an entity to the node.
        /// The entity and all his models / sound... will now move with the node.
        /// </summary>
        /// <param name="entity">The entity you want to attach.</param>
        /// <returns>Current node.</returns>
        public CNode AttachEntity(CEntity entity)
        {
            if (entity.Model != null)
                entity.Model.Node = this;
            foreach (CSprite3D sprite in entity.Sprite3D.Values)
                sprite.Node = this;
#if !WINDOWS_PHONE
            foreach (CSound sound in entity.Sound.Values)
                sound.Node = this;
#endif
            foreach (AEmitter emitter in entity.Emitter.Values)
                emitter.Node = this;
            m_entity = entity;
            m_entity.Node = this;
            if (m_entity.Physic != null)
                m_entity.AttachPhysic(m_entity.Physic);
            return this;
        }

        /// <summary>
        /// Detach the entity of the node if it had one.
        /// </summary>
        /// <returns>Current node.</returns>
        public CNode DetachEntity()
        {
            if (m_entity == null)
                return this;
            if (m_entity.Model != null)
                m_entity.Model.Node = null;
            foreach (CSprite3D sprite in m_entity.Sprite3D.Values)
                sprite.Node = null;
#if !WINDOWS_PHONE
            foreach (CSound sound in m_entity.Sound.Values)
                sound.Node = null;
#endif
            foreach (AEmitter emitter in m_entity.Emitter.Values)
                emitter.Node = null;
            m_entity.Node = null;
            m_entity = null;
            return this;
        }

        /// <summary>
        /// Create an entity if the node didn't have one.
        /// </summary>
        /// <returns>The created entity.</returns>
        public CEntity CreateEntity()
        {
            return m_entity == null ? new CEntity(this) : m_entity;
        }

        public void Remove()
        {
            if (m_render3d != null)
                m_render3d.RemoveNode(this);
        }

    #endregion

    #region REVIEW link

        /// <summary>
        /// Create a child's node. His position and orientation will be the same as his parent.
        /// </summary>
        /// <param name="type">Child node's type.</param>
        /// <param name="createEntity">Create or not the child's entity.</param>
        /// <returns>The created child.</returns>
        public CNode CreateChild(EType type, bool createEntity)
        {
            CNode child = new CNode(type, createEntity);

            child.SetParent(this);
            if (m_render3d != null)
                m_render3d.AddNode(child);
            return child;
        }

        public CNode CreateChild()
        {
            return CreateChild(EType.STATIC, true);
        }

        public CNode CreateChild(EType type)
        {
            return CreateChild(type, true);
        }

        /// <summary>
        /// Create a child's node. His position and orientation will be the same as his parent.
        /// </summary>
        /// <param name="name">Child's name.</param>
        /// <param name="type">Child's type.</param>
        /// <param name="createEntity">Create or not the child's entity.</param>
        /// <returns>The created child.</returns>
        public CNode CreateChild(string name, EType type, bool createEntity)
        {
            CNode child = new CNode(name, type, createEntity);
            
            child.SetParent(this);
            if (m_render3d != null)
                m_render3d.AddNode(child);
            return child;
        }

        public CNode CreateChild(string name, EType type)
        {
            return CreateChild(name, type, true);
        }

        public CNode CreateChild(string name)
        {
            return CreateChild(name, EType.STATIC, true);
        }

        /// <summary>
        /// Get a child node by his name.
        /// Null is returned of the node does not exists.
        /// </summary>
        /// <param name="name">Name of the child to get.</param>
        /// <returns>The child node.</returns>
        public CNode GetChild(string name)
        {
            return m_children.ContainsKey(name) ? m_children[name] : null;
        }

        /// <summary>
        /// Attach a child to this node.
        /// The child's tranformations will now be relative to his parent by default.
        /// </summary>
        /// <param name="child">Child to attach.</param>
        /// <returns>The child.</returns>
        public CNode AttachChild(CNode child)
        {
            if (child.Render3D == null)
                m_render3d.AddNode(child);
            if (!m_children.ContainsValue(child))
                child.SetParent(this);
            return child;
        }

        /// <summary>
        /// Attach a child node to this node by his name.
        /// The child's transformations will now be relative to his parent by default.
        /// </summary>
        /// <param name="childName">Name of the child to attach.</param>
        /// <returns>The child.</returns>
        public CNode AttachChild(string childName)
        {
            return AttachChild(m_render3d.GetNode(childName));
        }

        /// <summary>
        /// Detach the node from his current render 3d.
        /// </summary>
        /// <returns>The node.</returns>
        public CNode Detach()
        {
            if (m_render3d != null)
                SetParent(m_render3d.OriginNode);
            else
                SetParent(CRender3D.RefNode);
            return this;
        }

        /// <summary>
        /// Set the parent of the node.
        /// The node will be moved relative to his parent's position.
        /// </summary>
        /// <param name="parent">The node to set parent.</param>
        /// <returns>The node.</returns>
        public CNode SetParent(CNode parent, bool keepActualTransform)
        {
            if (parent == null)
            {
                if (m_parent != null)
                    m_parent.Children.Remove(m_name);
                m_parent = null;
            }
            else if (!this.Equals(parent))
            {
                if (parent != null)
                {
                    if (m_parent != null)
                        m_parent.Children.Remove(m_name);
                    m_parent = parent;
                    m_parent.Children.Add(m_name, this);

                    // able the static nodes to move by setting it to dynamic
                    bool stat = m_type == EType.STATIC;
                    if (stat)
                        Type = EType.DYNAMIC;

                    // save the actual transforms
                    Vector3 position = Position;
                    Quaternion orientation = Orientation;
                    Vector3 scale = NScale;

                    // update the transform relative to the new parent
                    _UpdateTransform(true, true, true);

                    // restore the old transforms in case of keepActualTransform is set
                    if (keepActualTransform)
                    {
                        Position = position;
                        Orientation = orientation;
                        NScale = scale;
                    }

                    // reset the static nodes to static
                    if (stat)
                        Type = EType.STATIC;
                }
            }
            return parent;
        }

        public CNode SetParent(CNode parent)
        {
            return SetParent(parent, false);
        }

        /// <summary>
        /// Set the parent of the node by his name.
        /// The node will now be moved relative to his parent's position.
        /// </summary>
        /// <param name="parentName">Name of the node to set parent.</param>
        /// <returns>The node.</returns>
        public CNode SetParent(string parentName, bool keepActualTransform)
        {
            return SetParent(m_render3d.GetNode(parentName), keepActualTransform);
        }

        public CNode SetParent(string parentName)
        {
            return SetParent(parentName, false);
        }

        /// <summary>
        /// Remove a child's node.
        /// </summary>
        /// <param name="child">Child's node to remove.</param>
        /// <returns>The removed child.</returns>
        public CNode RemoveChild(CNode child)
        {
            if (m_children.ContainsValue(child))
                child.Remove();
            return child;
        }

        /// <summary>
        /// Remove a child by his name.
        /// </summary>
        /// <param name="childName">Name of the child to remove.</param>
        /// <returns>The removed child.</returns>
        public CNode RemoveChild(string childName)
        {
            return RemoveChild(m_render3d.GetNode(childName));
        }

        /// <summary>
        /// Show or hide a node.
        /// </summary>
        /// <param name="visible">Set visibility.</param>
        /// <returns>The node.</returns>
        public CNode SetVisible(bool visible)
        {
            if (this.Equals(m_render3d.OriginNode))
                return this;
            // set it to visible
            if (!m_visible && visible == true)
            {
                m_visible = true;
                m_drawable = true;
            }
            // set it not visible
            else if (m_visible && visible == false)
            {
                m_visible = false;
                m_drawable = false;
            }
            return this;
        }

        /// <summary>
        /// Remove all the children of the node.
        /// </summary>
        public void RemoveChildren()
        {
            while (m_children.Count > 0)
                m_children.First().Value.Remove();
        }

    #endregion

    #region physics set

        public bool IgnorePhysicsTransform
        {
            get { return m_ignorePhysicsTransform; }
            set
            {
                m_ignorePhysicsTransform = value;
                foreach (CNode child in m_children.Values)
                    child.IgnorePhysicsTransform = value;
            }
        }

        internal void SetPhysicsTransform(Vector3 position, Matrix orientation)
        {
            if (m_ignorePhysicsTransform)
                return;

            m_relativePosition = Vector3.Transform(position - m_parent.m_derivedPosition, 
                Quaternion.Inverse(m_parent.m_derivedOrientation)) / m_derivedScale;
            m_relativeOrientation = Quaternion.Inverse(m_parent.m_derivedOrientation) * 
                Quaternion.CreateFromRotationMatrix(orientation);
            m_setPhysicsTranformCalled = true;
            _UpdateTransform(true, true, false, true);
        }

    #endregion

    #region set orientation

        /// <summary>
        /// Set node's orientation in a specified space.
        /// </summary>
        /// <param name="orientation">Orientation.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetOrientation(Quaternion orientation, ESpace space)
        {
            if (m_parent == null) return this;
            if (!m_ignorePhysicsTransform) return this;

            orientation.Normalize();
            switch (space)
            {
                case ESpace.LOCAL:
                    Rotate(orientation, ESpace.LOCAL);
                    break;
                case ESpace.PARENT:
                    m_relativeOrientation = orientation;
                    _UpdateTransform(true, true, false);
                    break;
                case ESpace.WORLD:
                    m_relativeOrientation = Quaternion.Inverse(m_parent.m_derivedOrientation) * orientation;
                    _UpdateTransform(true, true, false);
                    break;
            }
            m_yawUpdated = false;
            m_pitchUpdated = false;
            m_rollUpdated = false;
            return this;
        }

        public CNode SetOrientation(Quaternion orientation)
        {
            return SetOrientation(orientation, ESpace.PARENT);
        }

        /// <summary>
        /// Set node's orientation in a specified space.
        /// </summary>
        /// <param name="matrix">Orientation matrix.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetOrientation(Matrix matrix, ESpace space)
        {
            return SetOrientation(Quaternion.CreateFromRotationMatrix(matrix), space);
        }

        public CNode SetOrientation(Matrix matrix)
        {
            return SetOrientation(matrix, ESpace.PARENT);
        }

        /// <summary>
        /// Set node's orientation around an axis, in a specified space.
        /// </summary>
        /// <param name="axis">Rotation axis.</param>
        /// <param name="angle">Rotation angle in radian.</param>
        /// <param name="space">Transformation axis.</param>
        /// <returns>Node.</returns>
        public CNode SetOrientation(Vector3 axis, float angle, ESpace space)
        {
            return SetOrientation(Quaternion.CreateFromAxisAngle(axis, angle), space);
        }

        public CNode SetOrientation(Vector3 axis, float angle)
        {
            return SetOrientation(axis, angle, ESpace.PARENT);
        }

        /// <summary>
        /// Set node's orientation on X, Y and Z axis in a specified space.
        /// </summary>
        /// <param name="yaw">Rotation on Y axis in radian.</param>
        /// <param name="pitch">Rotation on X axis in radian.</param>
        /// <param name="roll">Rotation on Z axis in radian.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetOrientation(float yaw, float pitch, float roll, ESpace space)
        {
            return SetOrientation(Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll), space);
        }

        public CNode SetOrientation(float yaw, float pitch, float roll)
        {
            return SetOrientation(yaw, pitch, roll, ESpace.PARENT);
        }

        /// <summary>
        /// Set node's yaw in a specified space.
        /// </summary>
        /// <param name="angle">Yaw angle in radian.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetYaw(float angle, ESpace space)
        {
            return SetOrientation(Quaternion.CreateFromYawPitchRoll(angle, NPitch, NRoll), space);
        }

        public CNode SetYaw(float angle)
        {
            return SetYaw(angle, ESpace.PARENT);
        }

        /// <summary>
        /// Set node's pitch in a specified space.
        /// </summary>
        /// <param name="angle">Pitch angle in radian.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetPitch(float angle, ESpace space)
        {
            return SetOrientation(Quaternion.CreateFromYawPitchRoll(NYaw, angle, NRoll), space);
        }

        public CNode SetPitch(float angle)
        {
            return SetPitch(angle, ESpace.PARENT);
        }

        /// <summary>
        /// Set node's roll in a specified space.
        /// </summary>
        /// <param name="angle">Roll angle in radian.</param>
        /// <param name="space">Transformation space</param>
        /// <returns>Node.</returns>
        public CNode SetRoll(float angle, ESpace space)
        {
            return SetOrientation(Quaternion.CreateFromYawPitchRoll(NYaw, NPitch, angle), space);
        }

        public CNode SetRoll(float angle)
        {
            return SetRoll(angle, ESpace.PARENT);
        }

    #endregion

    #region set position

        /// <summary>
        /// Set the node's position relative to the specified space.
        /// </summary>
        /// <param name="position">Position.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetPosition(Vector3 position, ESpace space)
        {
            if (m_parent == null) return this;
            if (!m_ignorePhysicsTransform) return this;

            switch (space)
            {
                case ESpace.LOCAL:
                    Translate(position, ESpace.LOCAL);
                    break;
                case ESpace.PARENT:
                    m_relativePosition = position;
                    _UpdateTransform(false, true, false);
                    break;
                case ESpace.WORLD:
                    m_relativePosition = Vector3.Transform(position - m_parent.m_derivedPosition,
                        Quaternion.Inverse(m_parent.m_derivedOrientation)) / m_derivedScale;
                    //m_relativePosition = position - Vector3.Transform(m_parent.m_derivedPosition,
                    //    Quaternion.Inverse(m_parent.m_derivedOrientation)) / m_derivedScale;
                    _UpdateTransform(false, true, false);
                    break;
            }
            return this;
        }

        public CNode SetPosition(Vector3 position)
        {
            return SetPosition(position, ESpace.PARENT);
        }

        /// <summary>
        /// Set the node's position relative to the specified space.
        /// </summary>
        /// <param name="x">Position on X axis.</param>
        /// <param name="y">Position on Y axis.</param>
        /// <param name="z">Position on Z axis.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetPosition(float x, float y, float z, ESpace space)
        {
            return SetPosition(new Vector3(x, y, z), space);
        }

        public CNode SetPosition(float x, float y, float z)
        {
            return SetPosition(x, y, z, ESpace.PARENT);
        }

        /// <summary>
        /// Set the node's position on X axis relative to the specified space.
        /// </summary>
        /// <param name="x">Position on X axis.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetPositionX(float x, ESpace space)
        {
            return SetPosition(new Vector3(x, m_derivedPosition.Y, m_derivedPosition.Z), space);
        }

        public CNode SetPositionX(float x)
        {
            return SetPositionX(x, ESpace.PARENT);
        }

        /// <summary>
        /// Set the node's position on Y axis relative to the specified space.
        /// </summary>
        /// <param name="y">Position on Y axis.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetPositionY(float y, ESpace space)
        {
            return SetPosition(new Vector3(m_derivedPosition.X, y, m_derivedPosition.Z), space);
        }

        public CNode SetPositionY(float y)
        {
            return SetPositionY(y, ESpace.PARENT);
        }

        /// <summary>
        /// Set the node's position on Z axis relative to the specified space.
        /// </summary>
        /// <param name="z">Position on Z axis.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode SetPositionZ(float z, ESpace space)
        {
            return SetPosition(new Vector3(m_derivedPosition.X, m_derivedPosition.Y, z), space);
        }

        public CNode SetPositionZ(float z)
        {
            return SetPositionZ(z, ESpace.PARENT);
        }

    #endregion

    #region set scale

        /// <summary>
        /// Set the node's scale relative to parent.
        /// </summary>
        /// <param name="scale">Scale value.</param>
        /// <returns>Node.</returns>
        public CNode SetScale(Vector3 scale)
        {
            if (m_parent == null) return this;
            m_relativeScale = scale;
            _UpdateTransform(false, true, true);
            return this;
        }

        /// <summary>
        /// Set the node's scale relative to parent.
        /// </summary>
        /// <param name="x">Scale value on X axis.</param>
        /// <param name="y">Scale value on Y axis.</param>
        /// <param name="z">Scale value on Z axis.</param>
        /// <returns>Node.</returns>
        public CNode SetScale(float x, float y, float z)
        {
            return SetScale(new Vector3(x, y, z));
        }

        /// <summary>
        /// Set the node's scale with the same value on each axis, relative to parent.
        /// </summary>
        /// <param name="scale">Scale value.</param>
        /// <returns>Node.</returns>
        public CNode SetScale(float scale)
        {
            return SetScale(new Vector3(scale, scale, scale));
        }

        /// <summary>
        /// Set the node's scale on X axis.
        /// </summary>
        /// <param name="x">Scale value on X axis.</param>
        /// <returns>Node.</returns>
        public CNode SetScaleX(float x)
        {
            return SetScale(new Vector3(x, m_relativeScale.Y, m_relativeScale.Z));
        }

        /// <summary>
        /// Set the node's scale on Y axis.
        /// </summary>
        /// <param name="y">Scale value on Y axis.</param>
        /// <returns>Node.</returns>
        public CNode SetScaleY(float y)
        {
            return SetScale(new Vector3(m_relativeScale.X, y, m_relativeScale.Z));
        }

        /// <summary>
        /// Set the node's scale on Z axis.
        /// </summary>
        /// <param name="z">Scale value on Z axis.</param>
        /// <returns>Node.</returns>
        public CNode SetScaleZ(float z)
        {
            return SetScale(new Vector3(m_relativeScale.X, m_relativeScale.Y, z));
        }

    #endregion

    #region rotate

        /// <summary>
        /// Rotate the node by the specified quaternion.
        /// </summary>
        /// <param name="orientation">Orientation.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Rotate(Quaternion orientation, ESpace space)
        {
            if (m_parent == null) return this;
            if (!m_ignorePhysicsTransform) return this;

            orientation.Normalize();
            switch (space)
            {
                case ESpace.LOCAL:
                    m_relativeOrientation = m_relativeOrientation * orientation;
                    break;
                case ESpace.PARENT:
                    m_relativeOrientation = orientation * m_relativeOrientation;
                    break;
                case ESpace.WORLD:
                    m_relativeOrientation = m_relativeOrientation * Quaternion.Inverse(m_derivedOrientation) *
                        orientation * m_derivedOrientation;
                    break;
            }
            _UpdateTransform(true, true, false);
            m_yawUpdated = false;
            m_pitchUpdated = false;
            m_rollUpdated = false;
            return this;
        }

        public CNode Rotate(Quaternion orientation)
        {
            return Rotate(orientation, ESpace.LOCAL);
        }

        /// <summary>
        /// Rotate the node by the specified matrix.
        /// </summary>
        /// <param name="matrix">Orientation matrix.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Rotate(Matrix matrix, ESpace space)
        {
            return Rotate(Quaternion.CreateFromRotationMatrix(matrix), space);
        }

        public CNode Rotate(Matrix matrix)
        {
            return Rotate(matrix, ESpace.LOCAL);
        }

        /// <summary>
        /// Rotate the node around an axis.
        /// </summary>
        /// <param name="axis">Rotation axis.</param>
        /// <param name="angle">Angle to rotate in radian.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Rotate(Vector3 axis, float angle, ESpace space)
        {
            return Rotate(Quaternion.CreateFromAxisAngle(axis, angle), space);
        }

        public CNode Rotate(Vector3 axis, float angle)
        {
            return Rotate(axis, angle, ESpace.LOCAL);
        }

        /// <summary>
        /// Rotate the node with yaw, pitch and roll.
        /// </summary>
        /// <param name="yaw">Yaw angle in radian.</param>
        /// <param name="pitch">Pitch angle in radian.</param>
        /// <param name="roll">Roll angle in radian.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Rotate(float yaw, float pitch, float roll, ESpace space)
        {
            return Rotate(Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll), space);
        }

        public CNode Rotate(float yaw, float pitch, float roll)
        {
            return Rotate(yaw, pitch, roll, ESpace.LOCAL);
        }

        /// <summary>
        /// Rotate the node on his Y axis.
        /// </summary>
        /// <param name="angle">Yaw angle in radian.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Yaw(float angle, ESpace space)
        {
            return Rotate(Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle), space);
        }

        public CNode Yaw(float angle)
        {
            return Yaw(angle, ESpace.LOCAL);
        }

        /// <summary>
        /// Rotate the node on his X axis.
        /// </summary>
        /// <param name="angle">Pitch angle in radian.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Pitch(float angle, ESpace space)
        {
            return Rotate(Quaternion.CreateFromAxisAngle(Vector3.UnitX, angle), space);
        }

        public CNode Pitch(float angle)
        {
            return Pitch(angle, ESpace.LOCAL);
        }

        /// <summary>
        /// Rotate the node on his Z axis.
        /// </summary>
        /// <param name="angle">Roll angle in radian.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Roll(float angle, ESpace space)
        {
            return Rotate(Quaternion.CreateFromAxisAngle(Vector3.UnitZ, angle), space);
        }

        public CNode Roll(float angle)
        {
            return Roll(angle, ESpace.LOCAL);
        }

    #endregion

    #region translate

        /// <summary>
        /// Translate the node relative to the specified space.
        /// </summary>
        /// <param name="distance">Distance to translate.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Translate(Vector3 distance, ESpace space)
        {
            if (m_parent == null) return this;
            if (!m_ignorePhysicsTransform) return this;

            switch (space)
            {
                case ESpace.LOCAL:
                    m_relativePosition += Vector3.Transform(distance, m_relativeOrientation);
                    break;
                case ESpace.PARENT:
                    m_relativePosition += distance;
                    break;
                case ESpace.WORLD:
                    m_relativePosition += Vector3.Transform(distance, Quaternion.Inverse(m_parent.m_derivedOrientation)) / 
                        m_parent.m_derivedScale;
                    break;
            }
            _UpdateTransform(false, true, false);
            return this;
        }

        public CNode Translate(Vector3 distance)
        {
            return Translate(distance, ESpace.PARENT);
        }

        /// <summary>
        /// Translate the node relative to the specified space.
        /// </summary>
        /// <param name="x">Translation on X.</param>
        /// <param name="y">Translation on Y.</param>
        /// <param name="z">Translation on Z.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Translate(float x, float y, float z, ESpace space)
        {
            return Translate(new Vector3(x, y, z), space);
        }

        public CNode Translate(float x, float y, float z)
        {
            return Translate(x, y, z, ESpace.PARENT);
        }

        /// <summary>
        /// Translate the node by a specified orientation and distance.
        /// </summary>
        /// <param name="orientation">Orientation of the distance.</param>
        /// <param name="distance">Distance to translate.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Translate(Quaternion orientation, Vector3 distance, ESpace space)
        {
            return Translate(Vector3.Transform(distance, orientation), space);
        }

        public CNode Translate(Quaternion orientation, Vector3 distance)
        {
            return Translate(orientation, distance, ESpace.WORLD);
        }

        /// <summary>
        /// Translate the node by a specified orientation and distance.
        /// </summary>
        /// <param name="orientation">Orientation of the distance.</param>
        /// <param name="x">Translation on X.</param>
        /// <param name="y">Translation on Y.</param>
        /// <param name="z">Translation on Z.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode Translate(Quaternion orientation, float x, float y, float z, ESpace space)
        {
            return Translate(Vector3.Transform(new Vector3(x, y, z), orientation), space);
        }

        public CNode Translate(Quaternion orientation, float x, float y, float z)
        {
            return Translate(orientation, x, y, z, ESpace.WORLD);
        }

        /// <summary>
        /// Translate the node on X axis.
        /// </summary>
        /// <param name="x">Translation on X.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode TranslateX(float x, ESpace space)
        {
            return Translate(new Vector3(x, 0, 0), space);
        }

        public CNode TranslateX(float x)
        {
            return TranslateX(x, ESpace.PARENT);
        }

        /// <summary>
        /// Translate the node on Y axis.
        /// </summary>
        /// <param name="y">Translation on Y.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode TranslateY(float y, ESpace space)
        {
            return Translate(new Vector3(0, y, 0), space);
        }

        public CNode TranslateY(float y)
        {
            return TranslateY(y, ESpace.PARENT);
        }

        /// <summary>
        /// Translate the node on Z axis.
        /// </summary>
        /// <param name="z">Translation on Z.</param>
        /// <param name="space">Transformation space.</param>
        /// <returns>Node.</returns>
        public CNode TranslateZ(float z, ESpace space)
        {
            return Translate(new Vector3(0, 0, z), space);
        }

        public CNode TranslateZ(float z)
        {
            return TranslateZ(z, ESpace.PARENT);
        }

    #endregion

    #region scale

        /// <summary>
        /// Scale the node on X, Y and Z axis.
        /// </summary>
        /// <param name="scale">Scale amount per axis.</param>
        /// <returns>Node.</returns>
        public CNode Scale(Vector3 scale)
        {
            if (m_parent == null) return this;
            m_relativeScale *= scale;
            _UpdateTransform(false, true, true);
            return this;
        }

        /// <summary>
        /// Scale the node on X, Y and Z axis.
        /// </summary>
        /// <param name="x">Scale on X.</param>
        /// <param name="y">Scale on Y.</param>
        /// <param name="z">Scale on zZ</param>
        /// <returns>Node.</returns>
        public CNode Scale(float x, float y, float z)
        {
            return Scale(new Vector3(x, y, z));
        }

        /// <summary>
        /// Scale the node with same amount on each axis.
        /// </summary>
        /// <param name="scale">Scale amount.</param>
        /// <returns>Node.</returns>
        public CNode Scale(float scale)
        {
            return Scale(new Vector3(scale, scale, scale));
        }

        /// <summary>
        /// Scale node on his X axis.
        /// </summary>
        /// <param name="x">Scale amount.</param>
        /// <returns>Node.</returns>
        public CNode ScaleX(float x)
        {
            return Scale(new Vector3(x, 1, 1));
        }

        /// <summary>
        /// Scale node on his Y axis.
        /// </summary>
        /// <param name="y">Scale amount.</param>
        /// <returns>Node.</returns>
        public CNode ScaleY(float y)
        {
            return Scale(new Vector3(1, y, 1));
        }

        /// <summary>
        /// Scale node on his Z axis.
        /// </summary>
        /// <param name="z">Scale amount.</param>
        /// <returns>Node.</returns>
        public CNode ScaleZ(float z)
        {
            return Scale(new Vector3(1, 1, z));
        }

    #endregion

    #region other tranfoms

        /// <summary>
        /// Set the negative Z axis to point the target.
        /// The roll is locked so you can link a camera to it.
        /// </summary>
        /// <param name="target">Target to look at.</param>
        public void LookAt(Vector3 target)
        {
            if (target == m_derivedPosition)
                return;

            Vector3 direction = target - m_derivedPosition;

            // ---- orient on Y axis ----

            Vector3 dirxz = direction;
            dirxz.Y = 0;

            float yaw = 0;

            if (dirxz.LengthSquared() != 0)
            {
                dirxz.Normalize();

                float ag0 = (float)Math.Acos(-dirxz.Z);
                float ag1 = (float)Math.Asin(-dirxz.X);                
                if (float.IsNaN(ag0) || float.IsNaN(ag1)) // should display a warning !!!!
                    return;
                yaw = ag0 * Math.Sign(ag1);
            }
            Quaternion oriy = Quaternion.CreateFromYawPitchRoll(yaw, 0, 0);

            // ---- orient on X axis ----

            Vector3 diryz = Vector3.Transform(direction, Quaternion.Conjugate(oriy));
            diryz.X = 0;

            float pitch = 0;

            if (diryz.LengthSquared() != 0)
            {
                diryz.Normalize();

                float ag0 = (float)Math.Acos(-diryz.Z);
                float ag1 = (float)Math.Asin(-diryz.Y);                
                if (float.IsNaN(ag0) || float.IsNaN(ag1)) // should display a warning !!!!
                    return;
                pitch = ag0 * Math.Sign(-ag1);
            }
            Quaternion orix = Quaternion.CreateFromYawPitchRoll(0, pitch, 0);

            // ---- apply transform ----

            SetOrientation(oriy * orix, ESpace.WORLD);
        }

    #endregion

    #region update transform

        /// <summary>
        /// Apply specified transformations to the children.
        /// </summary>
        /// <param name="updateOrientation">Set to update orientation.</param>
        /// <param name="updatePosition">Set to update position.</param>
        /// <param name="updateScale">Set to update scale.</param>
        private void _UpdateTransform(bool updateOrientation, bool updatePosition, bool updateScale, bool calledByPhysic)
        {
            if (m_ignorePhysicsTransform || calledByPhysic)
            {
                bool stat = m_type == EType.STATIC;
                if (stat)
                    Type = EType.DYNAMIC;

                if (updateOrientation)
                    _UpdateOrientation(calledByPhysic);
                if (updatePosition)
                    _UpdatePosition(calledByPhysic);
                if (updateScale)
                    _UpdateScale(calledByPhysic);
                foreach (CNode child in m_children.Values)
                    child._UpdateTransform(updateOrientation, updatePosition, updateScale, calledByPhysic);

                if (stat)
                    Type = EType.STATIC;
            }
            m_setPhysicsTranformCalled = false;
        }

        private void _UpdateTransform(bool updateOrientation, bool updatePosition, bool updateScale)
        {
            _UpdateTransform(updateOrientation, updatePosition, updateScale, false);
        }

        /// <summary>
        /// Update the node's orientation.
        /// </summary>
        private void _UpdateOrientation(bool calledByPhysic)
        {
            m_derivedGoalOrientation = m_inheritOrientation ?
                m_parent.m_derivedOrientation * m_relativeOrientation : 
                m_relativeOrientation;
            m_derivedOrientation = m_smoothOrientation && !calledByPhysic ?
                Quaternion.Lerp(m_derivedOrientation, m_derivedGoalOrientation, m_smoothOrientationAmount * 
                CDare.Instance.Config.Time.TimeSinceLastFrame / 10) :
                m_derivedGoalOrientation;
        }

        private void _UpdateOrientation()
        {
            _UpdateOrientation(false);
        }

        /// <summary>
        /// Update the node's position.
        /// </summary>
        private void _UpdatePosition(bool calledByPhysic)
        {
            m_derivedGoalPosition = Vector3.Transform(m_parent.m_derivedScale * m_relativePosition, m_parent.m_derivedOrientation)  +
                m_parent.m_derivedPosition;
            m_derivedPosition = m_smoothPosition && !calledByPhysic ?
                Vector3.Lerp(m_derivedPosition, m_derivedGoalPosition, m_smoothPositionAmount * 
                CDare.Instance.Config.Time.TimeSinceLastFrame / 10) : 
                m_derivedGoalPosition;
        }

        private void _UpdatePosition()
        {
            _UpdatePosition(false);
        }

        /// <summary>
        /// Update the node's scale.
        /// </summary>
        private void _UpdateScale(bool calledByPhysic)
        {
            m_derivedGoalScale = m_inheritScale ? 
                m_parent.m_derivedScale * m_relativeScale : 
                m_relativeScale;
            m_derivedScale = m_smoothScale && !calledByPhysic ?
                Vector3.Lerp(m_derivedScale, m_derivedGoalScale, m_smoothScaleAmount * 
                CDare.Instance.Config.Time.TimeSinceLastFrame / 10) :
                m_derivedGoalScale;
        }
        private void _UpdateScale()
        {
            _UpdateScale(false);
        }
    #endregion

    }
}
